home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / Main.bin / SeedGenerator.java < prev    next >
Text File  |  1998-10-14  |  9KB  |  312 lines

  1. /*
  2.  * @(#)SeedGenerator.java    1.7 98/07/27
  3.  *
  4.  * Copyright 1995-1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.security;
  16.  
  17. /**
  18.  * <P> This class generates seeds for the cryptographically strong random
  19.  * number generator.
  20.  * <P> The seed is produced by counting the number of times the VM
  21.  * manages to loop in a given period. This number roughly
  22.  * reflects the machine load at that point in time.
  23.  * The samples are translated using a permutation (s-box)
  24.  * and then XORed together. This process is non linear and
  25.  * should prevent the samples from "averaging out". The s-box
  26.  * was designed to have even statistical distribution; it's specific
  27.  * values are not crucial for the security of the seed.
  28.  * We also create a number of sleeper threads which add entropy
  29.  * to the system by keeping the scheduler busy.
  30.  * Twenty such samples should give us roughly 160 bits of randomness.
  31.  * <P> These values are gathered in the background by a daemon thread
  32.  * thus allowing the system to continue performing it's different
  33.  * activites, which in turn add entropy to the random seed.
  34.  * <p> The class also gathers miscellaneous system information, some
  35.  * machine dependent, some not. This information is then hashed together
  36.  * with the 20 seed bytes.
  37.  *
  38.  * @version 1.7, 98/10/05
  39.  * @author Joshua Bloch
  40.  * @author Gadi Guy
  41.  */
  42.  
  43. import java.security.*;
  44. import java.io.*;
  45. import java.util.Properties;
  46. import java.util.Enumeration;
  47. import java.net.*;
  48.  
  49. class SeedGenerator implements Runnable {
  50.     // Static instance is created at link time
  51.     private static SeedGenerator myself = new SeedGenerator();
  52.  
  53.     // Queue is used to collect seed bytes
  54.     private byte[] pool;
  55.     private int start, end, count;
  56.  
  57.     // Thread group for our threads
  58.     ThreadGroup seedGroup;
  59.  
  60.     /**
  61.      * The constructor is only called once to construct the one
  62.      * instance we actually use. It instantiates the message digest
  63.      * and starts the thread going.
  64.      */
  65.  
  66.     private SeedGenerator() {
  67.     pool = new byte[20];
  68.     start = end = 0;
  69.  
  70.     Thread t = null;
  71.     MessageDigest digest;
  72.  
  73.     try {
  74.             digest = MessageDigest.getInstance("SHA");
  75.         } catch (NoSuchAlgorithmException e) {
  76.             throw new InternalError("internal error: SHA-1 not available.");
  77.     }
  78.  
  79.     ThreadGroup parent, group = Thread.currentThread().getThreadGroup();
  80.     while ((parent = group.getParent()) != null)
  81.         group = parent;
  82.     seedGroup = new ThreadGroup(group, "SeedGenerator ThreadGroup");
  83.     t = new Thread(seedGroup, this, "SeedGenerator Thread");
  84.     t.setPriority(Thread.MIN_PRIORITY);
  85.     t.setDaemon(true);
  86.     t.start();
  87.     }
  88.  
  89.     /**
  90.      * This method does the actual work. It collects random bytes and
  91.      * pushes them into the queue.
  92.      */
  93.  
  94.     final public void run() {
  95.     try {
  96.         while (true) {
  97.         // Queue full? Wait till there's room.
  98.         synchronized(this) {
  99.             while (count >= pool.length)
  100.             wait();
  101.         }
  102.  
  103.         int counter = 0;
  104.         byte v = 0;
  105.  
  106.         // Spin count must not be under 64000
  107.         while (counter < 64000) {
  108.  
  109.             // Start some noisy threads
  110.             try {
  111.             BogusThread bt = new BogusThread();
  112.             Thread t = new Thread
  113.                 (seedGroup, bt, "SeedGenerator Thread");
  114.             t.start();
  115.             } catch (Exception e) {
  116.             throw new InternalError("internal error: " +
  117.                 "SeedGenerator thread creation error.");
  118.             }
  119.  
  120.             // We wait 250milli quanta, so the minimum wait time
  121.             // cannot be under 250milli.
  122.             int latch = 0;
  123.             latch = 0;
  124.             long l = System.currentTimeMillis() + 250;
  125.             while (System.currentTimeMillis() < l) {
  126.             synchronized(this){};
  127.             latch++;
  128.             }
  129.  
  130.             // Translate the value using the permutation, and xor
  131.             // it with previous values gathered.
  132.             v ^= rndTab[latch % 255];
  133.             counter += latch;
  134.         }
  135.  
  136.         // Push it into the queue and notify anybody who might
  137.         // be waiting for it.
  138.         synchronized(this) {
  139.             pool[end] = v;
  140.             end++;
  141.             count++;
  142.             if (end >= pool.length)
  143.             end = 0;
  144.  
  145.             notifyAll();
  146.         }
  147.         }
  148.     } catch (Exception e) {
  149.             throw new InternalError("internal error: " +
  150.         "SeedGenerator thread generated an exception.");
  151.     }
  152.     }
  153.  
  154.     /**
  155.     * Return a byte from the queue. Wait for it if it isn't ready.
  156.     */
  157.  
  158.     static public byte getByte() {
  159.     return myself._getByte();
  160.     }
  161.  
  162.     private byte _getByte() {
  163.     byte b = 0;
  164.  
  165.     try {
  166.         // Wait for it...
  167.         synchronized(this) {
  168.         while (count <= 0)
  169.             wait();
  170.         }
  171.     } catch (Exception e) {
  172.         if (count <= 0)
  173.         throw new InternalError("internal error: " +
  174.             "SeedGenerator thread generated an exception.");
  175.     }
  176.  
  177.     synchronized(this) {
  178.         // Get it from the queue
  179.         b = pool[start];
  180.         pool[start] = 0;
  181.         start++;
  182.         count--;
  183.         if (start == pool.length)
  184.         start = 0;
  185.  
  186.        // Notify the daemon thread, just in case it is
  187.        // waiting for us to make room in the queue.
  188.         notifyAll();
  189.     }
  190.  
  191.     return b;
  192.     }
  193.  
  194.     /**
  195.      * Retrieve some system information, hashed.
  196.      */
  197.  
  198.     static byte[] getSystemEntropy() {
  199.     String s;
  200.     String[] sa;
  201.     Class c;
  202.     Object o;
  203.     byte b;
  204.     byte[] ba;
  205.     Properties p;
  206.     Enumeration e;
  207.     File f;
  208.     MessageDigest md;
  209.  
  210.     try {
  211.             md = MessageDigest.getInstance("SHA");
  212.         } catch (NoSuchAlgorithmException nsae) {
  213.             throw new InternalError("internal error: SHA-1 not available.");
  214.     }
  215.  
  216.     // The current time in millis
  217.     b =(byte)System.currentTimeMillis();
  218.     md.update(b);
  219.  
  220.     // System properties can change from machine to machine
  221.     p = System.getProperties();
  222.     e = p.propertyNames();
  223.     while (e.hasMoreElements()) {
  224.         s =(String)e.nextElement();
  225.         md.update(s.getBytes());
  226.         md.update(p.getProperty(s).getBytes());
  227.     }
  228.  
  229.     try {
  230.         md.update(InetAddress.getLocalHost().toString().getBytes());
  231.     } catch (UnknownHostException uhe) {
  232.         md.update((byte)uhe.hashCode());
  233.     }
  234.  
  235.     // The temporary dir
  236.     try {
  237.         f = new File(p.getProperty("java.io.tmpdir"));
  238.         sa = f.list();
  239.         for(int i = 0; i < sa.length; i++)
  240.         md.update(sa[i].getBytes());
  241.     } catch (Exception ex) {
  242.         md.update((byte)ex.hashCode());
  243.     }
  244.  
  245.     // get Runtime memory stats
  246.     Runtime rt = Runtime.getRuntime();
  247.     b =(byte)rt.totalMemory();
  248.     md.update(b);
  249.     b =(byte)rt.freeMemory();
  250.     md.update(b);
  251.  
  252.     return md.digest();
  253.     }
  254.  
  255.     /*
  256.     // This method helps the test utility receive unprocessed seed bytes.
  257.     public static int genTestSeed() {
  258.     return myself.getByte();
  259.     }
  260.     */
  261.  
  262.     // The permutation was calculated by generating 64k of random
  263.     // data and using it to mix the trivial permutation.
  264.     // It should be evenly distributed. The specific values
  265.     // are not crucial to the security of this class.
  266.     private static byte[] rndTab = {
  267.     56, 30, -107, -6, -86, 25, -83, 75, -12, -64,
  268.     5, -128, 78, 21, 16, 32, 70, -81, 37, -51,
  269.     -43, -46, -108, 87, 29, 17, -55, 22, -11, -111,
  270.     -115, 84, -100, 108, -45, -15, -98, 72, -33, -28,
  271.     31, -52, -37, -117, -97, -27, 93, -123, 47, 126,
  272.     -80, -62, -93, -79, 61, -96, -65, -5, -47, -119,
  273.     14, 89, 81, -118, -88, 20, 67, -126, -113, 60,
  274.     -102, 55, 110, 28, 85, 121, 122, -58, 2, 45,
  275.     43, 24, -9, 103, -13, 102, -68, -54, -101, -104,
  276.     19, 13, -39, -26, -103, 62, 77, 51, 44, 111,
  277.     73, 18, -127, -82, 4, -30, 11, -99, -74, 40,
  278.     -89, 42, -76, -77, -94, -35, -69, 35, 120, 76,
  279.     33, -73, -7, 82, -25, -10, 88, 125, -112, 58,
  280.     83, 95, 6, 10, 98, -34, 80, 15, -91, 86,
  281.     -19, 52, -17, 117, 49, -63, 118, -90, 36, -116,
  282.     -40, -71, 97, -53, -109, -85, 109, -16, -3, 104,
  283.     -95, 68, 54, 34, 26, 114, -1, 106, -121, 3,
  284.     66, 0, 100, -84, 57, 107, 119, -42, 112, -61,
  285.     1, 48, 38, 12, -56, -57, 39, -106, -72, 41,
  286.     7, 71, -29, -59, -8, -38, 79, -31, 124, -124,
  287.     8, 91, 116, 99, -4, 9, -36, -78, 63, -49,
  288.     -67, -87, 59, 101, -32, 92, 94, 53, -41, 115,
  289.     -66, -70, -122, 50, -50, -22, -20, -18, -21, 23,
  290.     -2, -48, 96, 65, -105, 123, -14, -110, 69, -24,
  291.     -120, -75, 74, 127, -60, 113, 90, -114, 105, 46,
  292.     27, -125, -23, -44, 64
  293.     };
  294.  
  295.     /**
  296.      * This inner thread causes the thread scheduler to become 'noisy',
  297.      * thus adding entropy to the system load.
  298.      * At least one instance of this class is generated for every seed byte.
  299.      */
  300.  
  301.     private class BogusThread implements Runnable {
  302.     final public void run() {
  303.         try {
  304.         for(int i = 0; i < 100; i++)
  305.            Thread.sleep(10);
  306.         System.gc();
  307.         } catch (Exception e) {
  308.         }
  309.     }
  310.     }
  311. }
  312.